﻿using System;
using System.IO;
using System.Linq;
using BattleFury.Cartographer;
using BattleFury.Cartographer.Builder;
using BattleFury.Lib;
using BattleFury.Lib.Controls;
using BattleFury.Lib.Graphics;
using BattleFury.Lib.Lights;
using BattleFury.Lib.Maps;
using BattleFury.Lib.Screens;
using BattleFury.Lib.Serialization;
using BattleFury.Lib.Tiles;
using BattleFury.Lib.Time;
using BattleFury.Time;
using Cartographer.Builder;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Console = BattleFury.Lib.Controls.Console;
using Vector2 = Microsoft.Xna.Framework.Vector2;

namespace BattleFury.Screens
{
	public class GameplayScreen
		: Screen
	{
		#region Variables / Properties

		private LightLayer _lightLayer;
		private UserInterface ui;
		private Console _console;

		private Camera _camera;
		private float _cameraSpeed = 8f;

		private bool _showWorldVisualization;
		private Texture2D _heightMap;
		private Texture2D _visualizationMap;

		#endregion

		#region Initialization

		public GameplayScreen()
		{
			OldRenderData.World = new BattleFuryWorld();

			_camera = new Camera();
			_camera.Size = RenderEngine.Get().Resolution;
		}

		public override void LoadContent()
		{
			OldRenderData.Tilesets.Add("Tileset", new Tileset(Content.Load<Texture2D>(".\\gfx\\Tilesets\\Tileset"), OldRenderData.TileSize));
			var tilesetTexture = OldRenderData.Tilesets["Tileset"].Texture;

			OldRenderData.Map = new TileMap(OldRenderData.MapSize);

			var groundLayer = OldRenderData.Map.AddTileLayer(OldRenderData.TileSize);
			groundLayer.Name = "Ground";
			groundLayer.Randomize(tilesetTexture, 64, 69); //64, 79);

			OldRenderData.LoadFromConfig(Content);

			//BlendStates.Initialize();
			_lightLayer = new LightLayer();

			foreach (var bot in OldRenderData.Bots)
			{
				var light = _lightLayer.AddLight(bot.Id.ToString(), Color.Yellow, 128f, bot.Position);
				light.Offset = bot.Size/2;
			}

			foreach (var player in OldRenderData.Players)
			{
				var light = _lightLayer.AddLight(player.Id.ToString(), Color.Yellow, 128f, player.Position);
				light.Offset = player.Size/2;
			}

			ui = new UserInterface();
			ui.Deserialize(this, ".\\Content\\ui\\default.xaml");
			var clock = (CelestialClock)ui.GetControl("Clock");
			clock.WorldTime = OldRenderData.World.WorldTime;
			_console = (Console) ui.GetControl("Console");
		}

		private void GenerateWorld()
		{
			_console.WriteMessage("Generating World...", Color.LimeGreen);
			try
			{
				WorldBuilder.Initialize(RenderEngine.Get().GraphicsDevice);

				WorldBuilder.Settings.Width = 880;
				WorldBuilder.Settings.Height = 880;

				_heightMap = WorldBuilder.HeightMap;
				_visualizationMap = WorldBuilder.BuildVisualizationTexture(VisualizationScale.Continents);

				var tileMap = WorldBuilder.BuildTileMap();
				OldRenderData.Map = tileMap;

				var cx = WorldBuilder.Settings.Width * OldRenderData.TileSize.X - RenderEngine.Get().Resolution.X;
				var cy = WorldBuilder.Settings.Height * OldRenderData.TileSize.Y - RenderEngine.Get().Resolution.Y;
				_camera.UpperBounds = new Vector2(cx, cy);

				_console.WriteMessage("World Generation Complete.", Color.LimeGreen);
				_console.WriteMessage("Seed: " + WorldBuilder.Settings.Seed);
			}
			catch (Exception e)
			{
				_console.WriteMessage("World Generation Failed");
				_console.WriteMessage(e.Message, Color.Red);
			}
		}

		#endregion

		#region Update

		public override void Update(GameTime gameTime)
		{
			if (!IsEnabled) return;

			if (RenderEngine.Get().Input.Keyboard.IsKeyPressed(Keys.Escape)) RenderEngine.Get().Exit();

			if (OldRenderData.Errors.Count > 0) return;

			ui.Update(gameTime);

			UpdateInput();

			OldRenderData.Map.Update(gameTime);

			foreach (var player in OldRenderData.Players)
			{
				player.Update(gameTime);
				_lightLayer[player.Id.ToString()].Position = player.Position;
			}

			foreach (var bot in OldRenderData.Bots)
			{
				bot.Update(gameTime);
				_lightLayer[bot.Id.ToString()].Position = bot.Position;
			}

			_lightLayer.AmbientLightColor = ((BattleFuryWorldTime)OldRenderData.World.WorldTime).LightColor.Color;
			_lightLayer.Update(gameTime);

			OldRenderData.World.Update(gameTime);

			base.Update(gameTime);
		}

		private void UpdateInput()
		{
			var input = RenderEngine.Get().Input;

			if (input.Keyboard.IsKeyPressed(Keys.F1))
			{
				foreach (var bot in OldRenderData.Bots)
				{
					bot.ShowBounds = !bot.ShowBounds;
				}
				foreach (var player in OldRenderData.Players)
				{
					player.ShowBounds = !player.ShowBounds;
				}
				foreach (var layer in OldRenderData.Map.Layers)
				{
					if (layer is CollisionLayer)
					{
						layer.IsVisible = !layer.IsVisible;
					}
				}
			}

			if (input.Keyboard.IsKeyPressed(Keys.F2)) LoadMap();
			if (input.Keyboard.IsKeyPressed(Keys.F3)) LoadTiledMap();
			if (input.Keyboard.IsKeyPressed(Keys.F4)) ToggleViewRange();
			if (input.Keyboard.IsKeyPressed(Keys.F7)) RenderEngine.Get().NetworkClient.Connect();
			if (input.Keyboard.IsKeyPressed(Keys.F8)) RenderEngine.Get().NetworkClient.Disconnect();
			if (input.Keyboard.IsKeyPressed(Keys.F9)) _showWorldVisualization = !_showWorldVisualization;
			if (input.Keyboard.IsKeyPressed(Keys.F10)) GenerateWorld();


			if (input.Keyboard.IsKeyHeld(Keys.Up)) _camera.Move(new Vector2(0f, -_cameraSpeed));
			if (input.Keyboard.IsKeyHeld(Keys.Down)) _camera.Move(new Vector2(0f, _cameraSpeed));
			if (input.Keyboard.IsKeyHeld(Keys.Left)) _camera.Move(new Vector2(-_cameraSpeed, 0f));
			if (input.Keyboard.IsKeyHeld(Keys.Right)) _camera.Move(new Vector2(_cameraSpeed, 0f));

			OldRenderData.Map.Camera = _camera;
		}

		private void ToggleViewRange()
		{
			foreach (var bot in OldRenderData.Bots)
			{
				bot.FieldOfViewIsVisible = !bot.FieldOfViewIsVisible;
			}
		}

		#endregion

		#region Draw

		public override void Draw(GameTime gameTime)
		{
			if (!IsVisible) return;

			RenderEngine.Get().GraphicsDevice.Clear(Color.Black);

			SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.AnisotropicClamp, null, null, null, _camera.Matrix);

			if (OldRenderData.Errors.Count > 0)
			{
				DrawErrors();
			}
			else
			{
				DrawScreen(gameTime);
			}

			SpriteBatch.End();

			SpriteBatch.Begin();

			DrawHUD();

			DrawWorldVisualizations();

			SpriteBatch.End();

			ui.Draw(gameTime);

			base.Draw(gameTime);
		}

		private void DrawErrors()
		{
			var line = 0;
			var font = OldRenderData.Fonts["Default"];
			foreach (var error in OldRenderData.Errors)
			{
				SpriteBatch.DrawString(font, error, new Vector2(8, line), Color.Red);
				line += 32;
			}
		}

		private void DrawScreen(GameTime gameTime)
		{
			//GameData.Map.Draw(gameTime, SpriteBatch);
			DrawUnderlayer(gameTime);

			//SpriteBatch.End();
			//_lightLayer.Draw(gameTime, SpriteBatch);
			//SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.AnisotropicClamp, null, null, null);

			foreach (var player in OldRenderData.Players)
			{
				player.Draw(gameTime);
			}

			foreach (var bot in OldRenderData.Bots)
			{
				bot.Draw(gameTime);
			}

			DrawOverlayer(gameTime);

			SpriteBatch.End();
			_lightLayer.Draw(gameTime, SpriteBatch);
			SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.AnisotropicClamp, null, null, null, _camera.Matrix);
		}

		private void DrawUnderlayer(GameTime gameTime)
		{
			OldRenderData.Map.DrawLayer("Ground", gameTime, SpriteBatch);
			OldRenderData.Map.DrawLayer("Detail", gameTime, SpriteBatch);
		}

		private void DrawOverlayer(GameTime gameTime)
		{
			OldRenderData.Map.DrawLayer("Overlay", gameTime, SpriteBatch);
			OldRenderData.Map.DrawCollisionLayer("Collisions", gameTime, SpriteBatch);
		}

		private void DrawHUD()
		{
			var font = OldRenderData.Fonts["Default"];
			SpriteBatch.DrawString(font, OldRenderData.World.WorldTime.ToString(), Vector2.Zero, Color.Yellow);
		}

		private void DrawWorldVisualizations()
		{
			if (!_showWorldVisualization) return;
			if (_heightMap == null) return;
			if (_visualizationMap == null) return;
			SpriteBatch.Draw(_heightMap, new Rectangle(0, 0, _heightMap.Width / 4, _heightMap.Height / 4), null, Color.White);
			SpriteBatch.Draw(_visualizationMap, new Rectangle(_heightMap.Width / 4, 0, _visualizationMap.Width / 4, _visualizationMap.Height / 4), null, Color.White);
		}

		#endregion

		#region Load Methods

		private void LoadMap()
		{
			const string filename = ".\\map.xml";
			if (!File.Exists(filename)) return;

			var mapData = TileMapSerializer.Deserialize(filename);
			OldRenderData.Map = TileMap.LoadFrom(OldRenderData.Tilesets, mapData);
		}

		private void LoadTiledMap()
		{
			const string filename = ".\\TiledMap.tmx";
			if (!File.Exists(filename)) return;

			var adapter = new TiledAdapter(filename);
			var map = adapter.Adapt();

			foreach (var layer in map.Layers.OfType<TileLayer>())
			{
				for (var y = 0; y < layer.Size.Y; y++)
				{
					for (var x = 0; x < layer.Size.X; x++)
					{
						var tile = layer.Tiles[x, y];
						if (tile == null) continue;
						var assetName = Path.GetFileName(tile.TilesetAssetName).Split('.')[0];
						var assetPath = ".\\gfx\\tilesets\\" + assetName;
						if (OldRenderData.Tilesets.ContainsKey(assetName))
						{
							tile.Tileset = OldRenderData.Tilesets[assetName];
						}
						else
						{
							tile.Tileset = new Tileset(Content.Load<Texture2D>(assetPath), tile.Size);
							OldRenderData.Tilesets.Add(assetName, tile.Tileset);
						}
					}
				}
			}

			OldRenderData.Map = map;
		}

		#endregion

		#region User Interface Actions

		public void Button1_OnClick()
		{
			var console = (Console)ui.GetControl("Console");
			console.WriteMessage("Button clicked");
		}


		#endregion
	}
}